// cDibSection.cpp: implementation of the cDibSection class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "cDibSection.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

cDibSection::cDibSection()
{
	hDC=0;
	hDib=0;
	hBmpOld=0;
	DIBSectionBitsPtr=NULL;
	lpBI=NULL;
}

cDibSection::cDibSection(int w,int h,int cClrBits,int bFlipRowOrder,int bIsR5G6B5)
{
	hDC=0;
	hDib=0;
	hBmpOld=0;
	DIBSectionBitsPtr=NULL;
	lpBI=NULL;
	Create(w,h,cClrBits,bFlipRowOrder,bIsR5G6B5);
}

cDibSection::~cDibSection()
{
	ClearUp();
}

void cDibSection::ClearUp()
{
	if(hDC){
		if(hDib){
			SelectObject(hDC,hBmpOld);
			DeleteObject(hDib);
		}
		DeleteObject(hDC);
	}
	hDC=0;
	hDib=0;
	hBmpOld=0;
	DIBSectionBitsPtr=NULL;
	if(lpBI){
		free(lpBI);
		lpBI=NULL;
	}
}

int cDibSection::CreateDIB(HDC lHDC,int lWidth,int lHeight,HBITMAP &hDib,int cClrBits,int bFlipRowOrder,int bIsR5G6B5,void* lpPal)
{
	int i;
    if (cClrBits <= 1) 
        cClrBits = 1; 
    else if (cClrBits <= 4) 
        cClrBits = 4; 
    else if (cClrBits <= 8) 
        cClrBits = 8; 
    else if (cClrBits <= 16) 
        cClrBits = 16; 
    else if (cClrBits <= 24) 
        cClrBits = 24; 
    else cClrBits = 32; 
	if(cClrBits<=8)
		lpBI=(PBITMAPINFO) malloc(sizeof(BITMAPINFOHEADER) + 
                    sizeof(RGBQUAD) * (1<< cClrBits)); 
	else if(cClrBits==16 && bIsR5G6B5)//bitfield
		lpBI=(PBITMAPINFO) malloc(sizeof(BITMAPINFOHEADER) + 
                    sizeof(RGBQUAD) * 3); 
	else
		lpBI=(PBITMAPINFO) malloc(sizeof(BITMAPINFOHEADER)); 
	memset(lpBI,0,sizeof(BITMAPINFOHEADER));
	lpBI->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	lpBI->bmiHeader.biWidth=lWidth;
	lpBI->bmiHeader.biHeight=bFlipRowOrder?-lHeight:lHeight;
	lpBI->bmiHeader.biPlanes=1;
	lpBI->bmiHeader.biBitCount=cClrBits;
	lpBI->bmiHeader.biCompression=(cClrBits==16 && bIsR5G6B5)?BI_BITFIELDS:BI_RGB;
	BytesPerScanLine=(((lWidth*cClrBits+7)>>3)+3)&0xfffffffc;
	lpBI->bmiHeader.biSizeImage=BytesPerScanLine*lHeight;
	//init palette
	if(cClrBits==16 && bIsR5G6B5){
		*(int*)&(lpBI->bmiColors[0])=0xf800;
		*(int*)&(lpBI->bmiColors[1])=0x7e0;
		*(int*)&(lpBI->bmiColors[2])=0x1f;
	}else if(cClrBits==4){
		if(lpPal)
			memcpy(lpBI->bmiColors,lpPal,64);
		else //' Create Gray scale palette as default:
			for(i=0;i<16;i++)
				*(int*)&(lpBI->bmiColors[i])=i*0x111111;
	}else if(cClrBits==8){
		if(lpPal)
			memcpy(lpBI->bmiColors,lpPal,1024);
		else //' Create Gray scale palette as default:
			for(i=0;i<256;i++)
				*(int*)&(lpBI->bmiColors[i])=i*0x10101;
	}else if(cClrBits==1){
		if(lpPal)
			memcpy(lpBI->bmiColors,lpPal,8);
		else{ //' Create Gray scale palette as default:
			*(int*)&(lpBI->bmiColors[0])=0;
			*(int*)&(lpBI->bmiColors[1])=0xffffff;
		}
	}
	hDib=CreateDIBSection(lHDC,lpBI,DIB_RGB_COLORS,&DIBSectionBitsPtr,NULL,0);
	return (int)hDib;
}

int cDibSection::Create(int w, int h, int cClrBits, int bFlipRowOrder,int bIsR5G6B5)
{
	ClearUp();
	hDC=CreateCompatibleDC(0);
	if(hDC){
		if(CreateDIB(hDC,w,h,hDib,cClrBits,bFlipRowOrder,bIsR5G6B5,NULL)){
			hBmpOld = (HBITMAP)SelectObject(hDC, hDib);
			return TRUE;
		}else{
			DeleteObject(hDC);
			hDC=0;
		}
	}
	return FALSE;
}

int cDibSection::Width()
{
	if(lpBI)
		return lpBI->bmiHeader.biWidth;
	return 0;
}

int cDibSection::Height()
{
	int i;
	if(lpBI){
		i=lpBI->bmiHeader.biHeight;
		return (i<0)?-i:i;
	}
	return 0;
}

cDibSection::cDibSection(int w, int h, int cClrBits)
{
	cDibSection(w,h,cClrBits,FALSE,FALSE);
}

int cDibSection::Create(int w, int h, int cClrBits)
{
	return Create(w,h,cClrBits,FALSE,FALSE);
}

int cDibSection::SavePicture(LPCTSTR sFileName)
{
	HANDLE hFile;
	BITMAPFILEHEADER tBH;
	DWORD i;
	if(lpBI){
		memset(&tBH,0,sizeof(BITMAPFILEHEADER));
		tBH.bfType=0x4d42;//BM
		tBH.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
		if(lpBI->bmiHeader.biBitCount==16){
			if(lpBI->bmiHeader.biCompression==BI_BITFIELDS)
				tBH.bfOffBits+=3*sizeof(RGBQUAD);
		}else if(lpBI->bmiHeader.biBitCount<=8)
			tBH.bfOffBits+=(1<<lpBI->bmiHeader.biBitCount)*sizeof(RGBQUAD);
		tBH.bfSize=tBH.bfOffBits+lpBI->bmiHeader.biSizeImage;
		hFile=CreateFile(sFileName,GENERIC_READ | GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
		if(hFile){
			WriteFile(hFile,&tBH, 14, &i, NULL);
			WriteFile(hFile,lpBI, tBH.bfOffBits-sizeof(BITMAPFILEHEADER), &i, NULL);
			WriteFile(hFile,DIBSectionBitsPtr, lpBI->bmiHeader.biSizeImage , &i, NULL);
			CloseHandle(hFile);
			return TRUE;
		}
	}
	return FALSE;
}

int cDibSection::CreateFromHBitmap(HBITMAP hBmp)
{
	return CreateFromHBitmap(hBmp,0);
}

int cDibSection::CreateFromHBitmap(HBITMAP hBmp, int cClrBits)
{
	HDC lhDC;
	HBITMAP lhBmpOld;
	BITMAP tBMP;
	GetObject(hBmp,sizeof(BITMAP),&tBMP);
	if(!cClrBits) cClrBits=tBMP.bmBitsPixel;
	if(Create(tBMP.bmWidth,tBMP.bmHeight,cClrBits)){
		lhDC=CreateCompatibleDC(0);//??
		if(lhDC){
			//' Select the bitmap into the compatible DC:
			lhBmpOld = (HBITMAP)SelectObject(lhDC, hBmp);
			//check color depth
			if(cClrBits<=8 && tBMP.bmBitsPixel<=cClrBits){//indexed color && src<=dest
				GetDIBColorTable(lhDC,0,(1<<tBMP.bmBitsPixel),lpBI->bmiColors);
				if(tBMP.bmBitsPixel==cClrBits)
					GetDIBits(lhDC,hBmp,0,tBMP.bmHeight,DIBSectionBitsPtr,lpBI,DIB_RGB_COLORS);
				else BitBlt(hDC, 0, 0, tBMP.bmWidth, tBMP.bmHeight, lhDC, 0, 0, SRCCOPY);//LoadPictureBlt
				SetDIBColorTable(hDC, 0, (1<<tBMP.bmBitsPixel),lpBI->bmiColors);
			}else{
				//LoadPictureBlt
				BitBlt(hDC, 0, 0, tBMP.bmWidth, tBMP.bmHeight, lhDC, 0, 0, SRCCOPY);
			}
			SelectObject(lhDC, lhBmpOld);
			DeleteObject(lhDC);
			return TRUE;
		}
	}
	return FALSE;
}

int cDibSection::CreateIndexed(int w, int h, int cClrBits, int bFlipRowOrder, void *lpPal)
{
	ClearUp();
	hDC=CreateCompatibleDC(0);
	if(hDC){
		if(CreateDIB(hDC,w,h,hDib,cClrBits,bFlipRowOrder,0,lpPal)){
			hBmpOld = (HBITMAP)SelectObject(hDC, hDib);
			return TRUE;
		}else{
			DeleteObject(hDC);
			hDC=0;
		}
	}
	return FALSE;
}

void cDibSection::PaintPicture(HDC lhDC, int lDestLeft, int lDestTop, int lDestWidth, int lDestHeight, int lSrcLeft, int lSrcTop, DWORD eRop)
{
	if(lpBI){
		if(lDestWidth<=0) lDestWidth=lpBI->bmiHeader.biWidth;
		if(lDestHeight<=0) lDestHeight=Height();
	    BitBlt(lhDC, lDestLeft, lDestTop, lDestWidth, lDestHeight, hDC, lSrcLeft, lSrcTop, eRop);
	}
}

void cDibSection::PaintPicture(HDC lhDC, int lDestLeft, int lDestTop)
{
	PaintPicture(lhDC,lDestLeft,lDestTop,0,0,0,0,SRCCOPY);
}

void cDibSection::PaintPicture(HDC lhDC, int lDestLeft, int lDestTop, int lDestWidth, int lDestHeight)
{
	PaintPicture(lhDC,lDestLeft,lDestTop,lDestWidth,lDestHeight,0,0,SRCCOPY);
}

void cDibSection::PaintPicture(HDC lhDC, int lDestLeft, int lDestTop, int lDestWidth, int lDestHeight, int lSrcLeft, int lSrcTop)
{
	PaintPicture(lhDC,lDestLeft,lDestTop,lDestWidth,lDestHeight,lSrcLeft,lSrcTop,SRCCOPY);
}

void cDibSection::Cls()
{
	if(lpBI)
		BitBlt(hDC, 0, 0, lpBI->bmiHeader.biWidth, Height(), 0, 0, 0, BLACKNESS);
}

int cDibSection::GetColor(int Index)
{
	RGBQUAD clr;
	if(lpBI){
		if(lpBI->bmiHeader.biBitCount<=8){
			Index &= (1 << lpBI->bmiHeader.biBitCount) - 1;
			if(GetDIBColorTable(hDC,Index,1,&clr) == 1)
				return clr.rgbRed | (clr.rgbGreen<<8) | (clr.rgbBlue<<16);
		}
	}
	return 0;
}

int cDibSection::GetColors(int Index, int Count, RGBQUAD *lp)
{
	if(lpBI){
		if(lpBI->bmiHeader.biBitCount<=8){
			return GetDIBColorTable(hDC,Index,Count,lp);
		}
	}
	return 0;
}

int cDibSection::SetColor(int Index, int clr)
{
	if(lpBI){
		if(lpBI->bmiHeader.biBitCount<=8){
			Index &= (1 << lpBI->bmiHeader.biBitCount) - 1;
			clr=(clr&0xff00) | ((clr&0xff)<<16) | ((clr&0xff0000)>>16);
			return SetDIBColorTable(hDC,Index,1,(RGBQUAD*)&clr);
		}
	}
	return 0;
}

int cDibSection::SetColors(int Index, int Count, RGBQUAD *lp)
{
	if(lpBI){
		if(lpBI->bmiHeader.biBitCount<=8){
			return SetDIBColorTable(hDC,Index,Count,lp);
		}
	}
	return 0;
}
